#include <iostream>  // TODO for debugging, remove

#include <string>
#include <vector>


#include <boost/algorithm/string.hpp>

#include "FieldNameLookup.hpp"



// Create the map of XML <STRING> fields to FieldName and FieldNameType
FieldNameLookup::FieldNameLookup()
{
  // TypeString's map FieldNameType to String values
  // Setup the FieldNameType to String mappings
  this->addTypeString("Unknown", FieldNameType::Unknown);
  this->addTypeString("String", FieldNameType::String);
  this->addTypeString("Double", FieldNameType::Double);
  this->addTypeString("Int64", FieldNameType::Int64);
  this->addTypeString("Date", FieldNameType::Date);
  this->addTypeString("DateTime", FieldNameType::DateTime);
  this->addTypeString("MultipleValues", FieldNameType::MultipleValues);
  this->addTypeString("LargeNumber", FieldNameType::LargeNumber);
  
  
  // Setup the FieldName to String mappings, with types
  this->addRelationship("Open",          FieldName::Open,        FieldNameType::Double);
  this->addRelationship("High",          FieldName::High,        FieldNameType::Double);
  this->addRelationship("Low",           FieldName::Low,         FieldNameType::Double);
  this->addRelationship("Close",         FieldName::Close,       FieldNameType::Double);
  this->addRelationship("Adj_Close",     FieldName::Adj_Close,   FieldNameType::Double);
  this->addRelationship("Volume",        FieldName::Volume,      FieldNameType::Int64);
  this->addRelationship("Date",          FieldName::Record_Date, FieldNameType::Date);
  this->addRelationship("Ask",           FieldName::Ask,         FieldNameType::Double);
  
  
  this->addRelationship("AverageDailyVolume",   FieldName::AverageDailyVolume, FieldNameType::Int64);
  this->addRelationship("Bid",                  FieldName::Bid,         FieldNameType::Double);
  this->addRelationship("AskRealtime",          FieldName::AskRealtime,         FieldNameType::Double);
  this->addRelationship("BidRealtime",          FieldName::BidRealtime,         FieldNameType::Double);
  this->addRelationship("BookValue",            FieldName::BookValue,         FieldNameType::Double);
  this->addRelationship("Change_PercentChange", FieldName::Change_PercentChange,         FieldNameType::MultipleValues);
  this->addRelationship("Change",               FieldName::Change,         FieldNameType::Double);
  this->addRelationship("Commission",           FieldName::Commission,   FieldNameType::Unknown);
  this->addRelationship("Currency",             FieldName::Currency, FieldNameType::String);
  this->addRelationship("ChangeRealtime",       FieldName::ChangeRealtime, FieldNameType::Double);
  this->addRelationship("AfterHoursChangeRealtime",         FieldName::AfterHoursChangeRealtime, FieldNameType::MultipleValues);
  this->addRelationship("DividendShare",        FieldName::DividendShare, FieldNameType::Double);
  this->addRelationship("LastTradeDate",        FieldName::LastTradeDate, FieldNameType::Date);
  this->addRelationship("TradeDate",            FieldName::TradeDate, FieldNameType::Date);  // TODO check
  this->addRelationship("EarningsShare",        FieldName::EarningsShare, FieldNameType::Double);
  this->addRelationship("ErrorIndicationreturnedforsymbolchangedinvalid",         FieldName::ErrorIndicationreturnedforsymbolchangedinvalid);
  this->addRelationship("EPSEstimateCurrentYear",    FieldName::EPSEstimateCurrentYear, FieldNameType::Double);
  this->addRelationship("EPSEstimateNextYear",       FieldName::EPSEstimateNextYear, FieldNameType::Double);
  this->addRelationship("EPSEstimateNextQuarter",    FieldName::EPSEstimateNextQuarter, FieldNameType::Double);
  this->addRelationship("DaysLow",              FieldName::DaysLow, FieldNameType::Double);
  this->addRelationship("DaysHigh",             FieldName::DaysHigh, FieldNameType::Double);
  this->addRelationship("YearLow",              FieldName::YearLow, FieldNameType::Double);
  this->addRelationship("YearHigh",             FieldName::YearHigh, FieldNameType::Double);
  this->addRelationship("HoldingsGainPercent",  FieldName::HoldingsGainPercent, FieldNameType::MultipleValues);
  this->addRelationship("AnnualizedGain",       FieldName::AnnualizedGain);
  this->addRelationship("HoldingsGain",         FieldName::HoldingsGain);
  this->addRelationship("HoldingsGainPercentRealtime",         FieldName::HoldingsGainPercentRealtime, FieldNameType::MultipleValues);
  this->addRelationship("HoldingsGainRealtime", FieldName::HoldingsGainRealtime);
  this->addRelationship("MoreInfo",             FieldName::MoreInfo);
  this->addRelationship("OrderBookRealtime",    FieldName::OrderBookRealtime);
  this->addRelationship("MarketCapitalization", FieldName::MarketCapitalization, FieldNameType::LargeNumber);
  this->addRelationship("MarketCapRealtime",    FieldName::MarketCapRealtime);
  this->addRelationship("EBITDA",               FieldName::EBITDA, FieldNameType::LargeNumber);
  this->addRelationship("ChangeFromYearLow",    FieldName::ChangeFromYearLow, FieldNameType::Double);
  this->addRelationship("PercentChangeFromYearLow",  FieldName::PercentChangeFromYearLow, FieldNameType::Double);
  this->addRelationship("LastTradeRealtimeWithTime", FieldName::LastTradeRealtimeWithTime, FieldNameType::MultipleValues);
  this->addRelationship("ChangePercentRealtime",     FieldName::ChangePercentRealtime, FieldNameType::MultipleValues);
  this->addRelationship("ChangeFromYearHigh",   FieldName::ChangeFromYearHigh, FieldNameType::Double);
  // NOTE: This really is 'Percebt', the typo is on yahoo's end
  this->addRelationship("PercebtChangeFromYearHigh", FieldName::PercebtChangeFromYearHigh, FieldNameType::Double);
  this->addRelationship("LastTradeWithTime",         FieldName::LastTradeWithTime, FieldNameType::MultipleValues);
  this->addRelationship("LastTradePriceOnly",        FieldName::LastTradePriceOnly, FieldNameType::Double);
  this->addRelationship("HighLimit",             FieldName::HighLimit);
  this->addRelationship("LowLimit",              FieldName::LowLimit);
  this->addRelationship("DaysRange",             FieldName::DaysRange, FieldNameType::MultipleValues);
  this->addRelationship("DaysRangeRealtime",     FieldName::DaysRangeRealtime, FieldNameType::MultipleValues);
  this->addRelationship("FiftydayMovingAverage", FieldName::FiftydayMovingAverage, FieldNameType::Double);
  this->addRelationship("TwoHundreddayMovingAverage",         FieldName::TwoHundreddayMovingAverage, FieldNameType::Double);
  this->addRelationship("ChangeFromTwoHundreddayMovingAverage",         FieldName::ChangeFromTwoHundreddayMovingAverage, FieldNameType::Double);
  this->addRelationship("PercentChangeFromTwoHundreddayMovingAverage",         FieldName::PercentChangeFromTwoHundreddayMovingAverage, FieldNameType::Double);
  this->addRelationship("ChangeFromFiftydayMovingAverage",         FieldName::ChangeFromFiftydayMovingAverage, FieldNameType::Double);
  this->addRelationship("PercentChangeFromFiftydayMovingAverage",         FieldName::PercentChangeFromFiftydayMovingAverage, FieldNameType::Double);
  this->addRelationship("Name",         FieldName::Name, FieldNameType::String);
  this->addRelationship("Notes",         FieldName::Notes);
  this->addRelationship("PreviousClose",         FieldName::PreviousClose, FieldNameType::Double);
  this->addRelationship("PricePaid",         FieldName::PricePaid, FieldNameType::Double);
  this->addRelationship("ChangeinPercent",         FieldName::ChangeinPercent, FieldNameType::Double);
  this->addRelationship("PriceSales",         FieldName::PriceSales, FieldNameType::Double);
  this->addRelationship("PriceBook",         FieldName::PriceBook, FieldNameType::Double);
  this->addRelationship("ExDividendDate",         FieldName::ExDividendDate);
  this->addRelationship("PERatio",         FieldName::PERatio, FieldNameType::Double);
  this->addRelationship("DividendPayDate",         FieldName::DividendPayDate);
  this->addRelationship("PERatioRealtime",         FieldName::PERatioRealtime);
  this->addRelationship("PEGRatio",         FieldName::PEGRatio, FieldNameType::Double);
  this->addRelationship("PriceEPSEstimateCurrentYear",         FieldName::PriceEPSEstimateCurrentYear, FieldNameType::Double);
  this->addRelationship("PriceEPSEstimateNextYear",         FieldName::PriceEPSEstimateNextYear, FieldNameType::Double);
  this->addRelationship("Symbol",         FieldName::Symbol, FieldNameType::String);
  this->addRelationship("SharesOwned",         FieldName::SharesOwned);
  this->addRelationship("ShortRatio",         FieldName::ShortRatio, FieldNameType::Double);
  this->addRelationship("LastTradeTime",         FieldName::LastTradeTime);
  this->addRelationship("TickerTrend",         FieldName::TickerTrend);
  this->addRelationship("OneyrTargetPrice",         FieldName::OneyrTargetPrice, FieldNameType::Double);
  this->addRelationship("HoldingsValue",         FieldName::HoldingsValue);
  this->addRelationship("HoldingsValueRealtime",         FieldName::HoldingsValueRealtime);
  this->addRelationship("YearRange",         FieldName::YearRange, FieldNameType::MultipleValues);
  this->addRelationship("DaysValueChange", FieldName::DaysValueChange, FieldNameType::MultipleValues);
  this->addRelationship("DaysValueChangeRealtime", FieldName::DaysValueChangeRealtime, FieldNameType::MultipleValues);
  this->addRelationship("StockExchange",           FieldName::StockExchange, FieldNameType::String);
  this->addRelationship("DividendYield", FieldName::DividendYield, FieldNameType::Double);
  this->addRelationship("PercentChange", FieldName::PercentChange, FieldNameType::Double);
}



void FieldNameLookup::addRelationship(std::string text, FieldName fieldName, FieldNameType fieldNameType)
{
  this->mapStringToFieldName.insert( MapStringToFieldNamePair(text, fieldName) );
  this->mapFieldNameToString.insert( MapFieldNameToStringPair(fieldName, text) );
  this->mapFieldNameToFieldNameType.insert( MapFieldNameToFieldNameTypePair(fieldName, fieldNameType) );
  
}


void FieldNameLookup::addTypeString(std::string text, FieldNameType fieldNameType)
{
  this->mapFieldNameTypeToString.insert(MapFieldNameTypeToStringPair(fieldNameType, text));
}



FieldName FieldNameLookup::StringToFieldName(std::string text)
{
  MapStringToFieldName::const_iterator itr = this->mapStringToFieldName.find(text);
  if (itr == this->mapStringToFieldName.end()) {
    // Not found, return Unknown
    return FieldName::Unknown;
  }
  
  return (itr->second);
}


std::string FieldNameLookup::FieldNameToString(FieldName fieldName)
{
  MapFieldNameToString::const_iterator itr = this->mapFieldNameToString.find(fieldName);
  if (itr == this->mapFieldNameToString.end()) {
    // Not found, return a zero length string
    return std::string("");
  }
  
  return (itr->second);
}

void FieldNameLookup::getFieldNamesWithType(FieldNameType fieldNameType, std::vector<std::string>& vecNames)
{
  vecNames.clear();
  
  for (MapFieldNameToFieldNameType::iterator itr = this->mapFieldNameToFieldNameType.begin();
       itr != this->mapFieldNameToFieldNameType.end();  ++itr)
  {
	 FieldName     itrFieldName     = itr->first;
	 FieldNameType itrFieldNameType = itr->second;
	 if (itrFieldNameType == fieldNameType) {
	   std::string strFieldName = this->FieldNameToString(itrFieldName);
	   vecNames.push_back(strFieldName);
	 }
       }
}

FieldNameType FieldNameLookup::FieldNameToFieldNameType(FieldName fieldName)
{
  MapFieldNameToFieldNameType::const_iterator itr = this->mapFieldNameToFieldNameType.find(fieldName);
  if (itr == this->mapFieldNameToFieldNameType.end()) {
    return FieldNameType::Unknown;  
  }
  
  return (itr->second);
}

std::string FieldNameLookup::FieldNameTypeToString(FieldNameType fieldNameType)
{
  MapFieldNameTypeToString::const_iterator itr = this->mapFieldNameTypeToString.find(fieldNameType);
  if (itr == this->mapFieldNameTypeToString.end()) {
    return "Unknown";  
  }
  
  return (itr->second);
}
